/**
 * Copyright (C) 2023 maminjie <canpool@163.com>
 * Copyright (C) 2017 Uwe Kindler
 * SPDX-License-Identifier: LGPL-2.1
 **/
#pragma once

#include "QxGlobal.h"
#include <QDebug>
#include <QMouseEvent>
#include <QPair>
#include <QPixmap>
#include <QStyle>
#include <QWidget>
#include <iostream>

#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
    #include <xcb/xcb.h>
#endif

QT_FORWARD_DECLARE_CLASS(QAbstractButton)

#ifndef QX_DOCK_STATIC
#ifdef  QX_DOCK_SHARED_EXPORT
#define QX_DOCK_EXPORT Q_DECL_EXPORT
#else
#define QX_DOCK_EXPORT Q_DECL_IMPORT
#endif
#else
#define QX_DOCK_EXPORT
#endif

// Define QX_DOCK_DEBUG_PRINT to enable a lot of debug output
#ifdef QX_DOCK_DEBUG_PRINT
#define QX_DOCK_PRINT(s) qDebug() << s
#else
#define QX_DOCK_PRINT(s)
#endif

// Set QX_DOCK_DEBUG_LEVEL to enable additional debug output and to enable layout
// dumps to qDebug and std::cout after layout changes
#define QX_DOCK_DEBUG_LEVEL 0

QT_FORWARD_DECLARE_CLASS(QSplitter)

QX_BEGIN_NAMESPACE

Q_NAMESPACE
class DockSplitter;

enum DockWidgetArea {
    NoDockWidgetArea = 0x00,
    LeftDockWidgetArea = 0x01,
    RightDockWidgetArea = 0x02,
    TopDockWidgetArea = 0x04,
    BottomDockWidgetArea = 0x08,
    CenterDockWidgetArea = 0x10,

    InvalidDockWidgetArea = NoDockWidgetArea,
    OuterDockAreas = TopDockWidgetArea | LeftDockWidgetArea | RightDockWidgetArea | BottomDockWidgetArea,
    AllDockAreas = OuterDockAreas | CenterDockWidgetArea
};
Q_DECLARE_FLAGS(DockWidgetAreas, DockWidgetArea)

enum TitleBarButton {
    TitleBarButtonTabsMenu,
    TitleBarButtonUndock,
    TitleBarButtonClose,
    TitleBarButtonAutoHide
};

/**
 * The different dragging states
 */
enum eDragState {
    DraggingInactive,        //!< DraggingInactive
    DraggingMousePressed,    //!< DraggingMousePressed
    DraggingTab,             //!< DraggingTab
    DraggingFloatingWidget   //!< DraggingFloatingWidget
};

/**
 * The different icons used in the UI
 */
enum eIcon {
    TabCloseIcon,         //!< TabCloseIcon
    AutoHideIcon,         //!< AutoHideIcon
    DockAreaMenuIcon,     //!< DockAreaMenuIcon
    DockAreaUndockIcon,   //!< DockAreaUndockIcon
    DockAreaCloseIcon,    //!< DockAreaCloseIcon

    IconCount,            //!< just a delimiter for range checks
};

/**
 * For bitwise combination of dock wdget features
 */
enum eBitwiseOperator {
    BitwiseAnd,
    BitwiseOr
};

/**
 * Each dock container supports 4 sidebars
 */
enum SideBarLocation {
    SideBarTop,
    SideBarLeft,
    SideBarRight,
    SideBarBottom,
    SideBarNone
};
Q_ENUMS(SideBarLocation);

namespace internal
{
static const bool RestoreTesting = true;
static const bool Restore = false;
static const char *const ClosedProperty = "close";
static const char *const DirtyProperty = "dirty";
extern const int FloatingWidgetDragStartEvent;
extern const int DockedWidgetDragStartEvent;

#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
// Utils to directly communicate with the X server
/**
 * Get atom from cache or request it from the XServer.
 */
xcb_atom_t xcb_get_atom(const char *name);

/**
 * Add a property to a window. Only works on "hidden" windows.
 */
void xcb_add_prop(bool state, WId window, const char *type, const char *prop);
/**
 * Updates up to two window properties. Can be set on a visible window.
 */
void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2 = nullptr);
/**
 * Only for debugging purposes.
 */
bool xcb_dump_props(WId window, const char *type);
/**
 * Gets the active window manager from the X11 Server.
 * Requires a EWMH conform window manager (Allmost all common used ones are).
 * Returns "UNKNOWN" otherwise.
 */
QString windowManager();
#endif

/**
 * Replace the from widget in the given splitter with the To widget
 */
void replaceSplitterWidget(QSplitter *Splitter, QWidget *From, QWidget *To);

/**
 * This function walks the splitter tree upwards to hides all splitters
 * that do not have visible content
 */
void hideEmptyParentSplitters(DockSplitter *FirstParentSplitter);

/**
 * Convenience class for QPair to provide better naming than first and
 * second
 */
class CDockInsertParam : public QPair<Qt::Orientation, bool>
{
public:
    using QPair<Qt::Orientation, bool>::QPair;
    Qt::Orientation orientation() const
    {
        return this->first;
    }
    bool append() const
    {
        return this->second;
    }
    int insertOffset() const
    {
        return append() ? 1 : 0;
    }
};

/**
 * Returns the insertion parameters for the given dock area
 */
CDockInsertParam dockAreaInsertParameters(DockWidgetArea Area);

/**
 * Searches for the parent widget of the given type.
 * Returns the parent widget of the given widget or 0 if the widget is not
 * child of any widget of type T
 *
 * It is not safe to use this function in in DockWidget because only
 * the current dock widget has a parent. All dock widgets that are not the
 * current dock widget in a dock area have no parent.
 */
template <class T> T findParent(const QWidget *w)
{
    QWidget *parentWidget = w->parentWidget();
    while (parentWidget) {
        T ParentImpl = qobject_cast<T>(parentWidget);
        if (ParentImpl) {
            return ParentImpl;
        }
        parentWidget = parentWidget->parentWidget();
    }
    return 0;
}

/**
 * Creates a semi transparent pixmap from the given pixmap Source.
 * The Opacity parameter defines the opacity from completely transparent (0.0)
 * to completely opaque (1.0)
 */
QPixmap createTransparentPixmap(const QPixmap &Source, qreal Opacity);

/**
 * Helper function for settings flags in a QFlags instance.
 */
template <class T> void setFlag(T &Flags, typename T::enum_type flag, bool on = true)
{
#if QT_VERSION >= 0x050700
    Flags.setFlag(flag, on);
#else
    if (on) {
        Flags |= flag;
    } else {
        Flags &= ~flag;
    }
#endif
}

/**
 * Helper function for settings tooltips without cluttering the code with
 * tests for preprocessor macros
 */
template <class QObjectPtr> void setToolTip(QObjectPtr obj, const QString &tip)
{
#ifndef QT_NO_TOOLTIP
    obj->setToolTip(tip);
#else
    Q_UNUSED(obj);
    Q_UNUSED(tip);
#endif
}

/**
 * Helper function for access to mouse event global position in Qt5 and
 */
inline QPoint globalPositionOf(QMouseEvent *ev)
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
    return ev->globalPosition().toPoint();
#else
    return ev->globalPos();
#endif
}

/**
 * Helper function to set the icon of a certain button.
 * Use this function to set the icons for the dock area and dock widget buttons.
 * The function first uses the CustomIconId to get an icon from the
 * DockIconProvider. You can register your custom icons with the icon provider, if
 * you do not want to use the default buttons and if you do not want to use
 * stylesheets.
 * If the IconProvider does not return a valid icon (icon is null), the function
 * fetches the given standard pixmap from the QStyle.
 * param[in] Button The button whose icons are to be set
 * param[in] StandardPixmap The standard pixmap to be used for the button
 * param[in] CustomIconId The identifier for the custom icon.
 */
void setButtonIcon(QAbstractButton *Button, QStyle::StandardPixmap StandarPixmap, Qx::eIcon CustomIconId);

enum eRepolishChildOptions {
    RepolishIgnoreChildren,
    RepolishDirectChildren,
    RepolishChildrenRecursively
};

/**
 * Calls unpolish() / polish for the style of the given widget to update
 * stylesheet if a property changes
 */
void repolishStyle(QWidget *w, eRepolishChildOptions Options = RepolishIgnoreChildren);

/**
 * Returns the geometry of the given widget in global space
 */
QRect globalGeometry(QWidget *w);

}   // namespace internal

QX_END_NAMESPACE

Q_DECLARE_OPERATORS_FOR_FLAGS(Qx::DockWidgetAreas)

